home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / transaction / _transaction.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  21.2 KB  |  651 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. """Transaction objects manage resources for an individual activity.
  5.  
  6. Compatibility issues
  7. --------------------
  8.  
  9. The implementation of Transaction objects involves two layers of
  10. backwards compatibility, because this version of transaction supports
  11. both ZODB 3 and ZODB 4.  Zope is evolving towards the ZODB4
  12. interfaces.
  13.  
  14. Transaction has two methods for a resource manager to call to
  15. participate in a transaction -- register() and join().  join() takes a
  16. resource manager and adds it to the list of resources.  register() is
  17. for backwards compatibility.  It takes a persistent object and
  18. registers its _p_jar attribute.  TODO: explain adapter
  19.  
  20. Subtransactions
  21. ---------------
  22.  
  23. Note: Subtransactions are deprecated!  Use savepoint/rollback instead.
  24.  
  25. A subtransaction applies the transaction notion recursively.  It
  26. allows a set of modifications within a transaction to be committed or
  27. aborted as a group.  A subtransaction is a strictly local activity;
  28. its changes are not visible to any other database connection until the
  29. top-level transaction commits.  In addition to its use to organize a
  30. large transaction, subtransactions can be used to optimize memory use.
  31. ZODB must keep modified objects in memory until a transaction commits
  32. and it can write the changes to the storage.  A subtransaction uses a
  33. temporary disk storage for its commits, allowing modified objects to
  34. be flushed from memory when the subtransaction commits.
  35.  
  36. The commit() and abort() methods take an optional subtransaction
  37. argument that defaults to false.  If it is a true, the operation is
  38. performed on a subtransaction.
  39.  
  40. Subtransactions add a lot of complexity to the transaction
  41. implementation.  Some resource managers support subtransactions, but
  42. they are not required to.  (ZODB Connection is the only standard
  43. resource manager that supports subtransactions.)  Resource managers
  44. that do support subtransactions implement abort_sub() and commit_sub()
  45. methods and support a second argument to tpc_begin().
  46.  
  47. The second argument to tpc_begin() indicates that a subtransaction
  48. commit is beginning (if it is true).  In a subtransaction, there is no
  49. tpc_vote() call, because sub-transactions don't need 2-phase commit.
  50. If a sub-transaction abort or commit fails, we can abort the outer
  51. transaction.  The tpc_finish() or tpc_abort() call applies just to
  52. that subtransaction.
  53.  
  54. Once a resource manager is involved in a subtransaction, all
  55. subsequent transactions will be treated as subtransactions until
  56. abort_sub() or commit_sub() is called.  abort_sub() will undo all the
  57. changes of the subtransactions.  commit_sub() will begin a top-level
  58. transaction and store all the changes from subtransactions.  After
  59. commit_sub(), the transaction must still call tpc_vote() and
  60. tpc_finish().
  61.  
  62. If the resource manager does not support subtransactions, nothing
  63. happens when the subtransaction commits.  Instead, the resource
  64. manager is put on a list of managers to commit when the actual
  65. top-level transaction commits.  If this happens, it will not be
  66. possible to abort subtransactions.
  67.  
  68. Two-phase commit
  69. ----------------
  70.  
  71. A transaction commit involves an interaction between the transaction
  72. object and one or more resource managers.  The transaction manager
  73. calls the following four methods on each resource manager; it calls
  74. tpc_begin() on each resource manager before calling commit() on any of
  75. them.
  76.  
  77.     1. tpc_begin(txn)
  78.     2. commit(txn)
  79.     3. tpc_vote(txn)
  80.     4. tpc_finish(txn)
  81.  
  82. Subtransaction commit
  83. ---------------------
  84.  
  85. Note: Subtransactions are deprecated!
  86.  
  87. When a subtransaction commits, the protocol is different.
  88.  
  89. 1. tpc_begin() is passed a second argument, which indicates that a
  90.    subtransaction is being committed.
  91. 2. tpc_vote() is not called.
  92.  
  93. Once a subtransaction has been committed, the top-level transaction
  94. commit will start with a commit_sub() call instead of a tpc_begin()
  95. call.
  96.  
  97. Before-commit hook
  98. ------------------
  99.  
  100. Sometimes, applications want to execute some code when a transaction is
  101. committed.  For example, one might want to delay object indexing until a
  102. transaction commits, rather than indexing every time an object is changed.
  103. Or someone might want to check invariants only after a set of operations.  A
  104. pre-commit hook is available for such use cases:  use addBeforeCommitHook(),
  105. passing it a callable and arguments.  The callable will be called with its
  106. arguments at the start of the commit (but not for substransaction commits).
  107.  
  108. Error handling
  109. --------------
  110.  
  111. When errors occur during two-phase commit, the transaction manager
  112. aborts all the resource managers.  The specific methods it calls
  113. depend on whether the error occurs before or after the call to
  114. tpc_vote() on that transaction manager.
  115.  
  116. If the resource manager has not voted, then the resource manager will
  117. have one or more uncommitted objects.  There are two cases that lead
  118. to this state; either the transaction manager has not called commit()
  119. for any objects on this resource manager or the call that failed was a
  120. commit() for one of the objects of this resource manager.  For each
  121. uncommitted object, including the object that failed in its commit(),
  122. call abort().
  123.  
  124. Once uncommitted objects are aborted, tpc_abort() or abort_sub() is
  125. called on each resource manager.
  126.  
  127. Synchronization
  128. ---------------
  129.  
  130. You can register sychronization objects (synchronizers) with the
  131. tranasction manager.  The synchronizer must implement
  132. beforeCompletion() and afterCompletion() methods.  The transaction
  133. manager calls beforeCompletion() when it starts a top-level two-phase
  134. commit.  It calls afterCompletion() when a top-level transaction is
  135. committed or aborted.  The methods are passed the current Transaction
  136. as their only argument.
  137. """
  138. import logging
  139. import sys
  140. import thread
  141. import warnings
  142. import weakref
  143. import traceback
  144. from cStringIO import StringIO
  145. from zope import interface
  146. from transaction import interfaces
  147. _marker = object()
  148.  
  149. def myhasattr(obj, attr):
  150.     return getattr(obj, attr, _marker) is not _marker
  151.  
  152.  
  153. class Status:
  154.     ACTIVE = 'Active'
  155.     COMMITTING = 'Committing'
  156.     COMMITTED = 'Committed'
  157.     COMMITFAILED = 'Commit failed'
  158.  
  159.  
  160. class Transaction(object):
  161.     interface.implements(interfaces.ITransaction, interfaces.ITransactionDeprecated)
  162.     _savepoint_index = 0
  163.     _savepoint2index = None
  164.     _subtransaction_savepoint = None
  165.     user = ''
  166.     description = ''
  167.     
  168.     def __init__(self, synchronizers = None, manager = None):
  169.         self.status = Status.ACTIVE
  170.         self._resources = []
  171.         if synchronizers is None:
  172.             WeakSet = WeakSet
  173.             import ZODB.utils
  174.             synchronizers = WeakSet()
  175.         
  176.         self._synchronizers = synchronizers
  177.         self._manager = manager
  178.         self._adapters = { }
  179.         self._voted = { }
  180.         self._extension = { }
  181.         self.log = logging.getLogger('txn.%d' % thread.get_ident())
  182.         self.log.debug('new transaction')
  183.         self._failure_traceback = None
  184.         self._before_commit = []
  185.  
  186.     
  187.     def _prior_operation_failed(self):
  188.         TransactionFailedError = TransactionFailedError
  189.         import ZODB.POSException
  190.         if not self._failure_traceback is not None:
  191.             raise AssertionError
  192.         raise TransactionFailedError('An operation previously failed, with traceback:\n\n%s' % self._failure_traceback.getvalue())
  193.  
  194.     
  195.     def join(self, resource):
  196.         if self.status is Status.COMMITFAILED:
  197.             self._prior_operation_failed()
  198.         
  199.         if self.status is not Status.ACTIVE:
  200.             raise ValueError("expected txn status %r, but it's %r" % (Status.ACTIVE, self.status))
  201.         
  202.         if myhasattr(resource, 'prepare'):
  203.             resource = DataManagerAdapter(resource)
  204.         
  205.         self._resources.append(resource)
  206.         if self._savepoint2index:
  207.             datamanager_savepoint = AbortSavepoint(resource, self)
  208.             for transaction_savepoint in self._savepoint2index.keys():
  209.                 transaction_savepoint._savepoints.append(datamanager_savepoint)
  210.             
  211.         
  212.  
  213.     
  214.     def savepoint(self, optimistic = False):
  215.         if self.status is Status.COMMITFAILED:
  216.             self._prior_operation_failed()
  217.         
  218.         
  219.         try:
  220.             savepoint = Savepoint(self, optimistic, *self._resources)
  221.         except:
  222.             self._cleanup(self._resources)
  223.             self._saveCommitishError()
  224.  
  225.         if self._savepoint2index is None:
  226.             self._savepoint2index = weakref.WeakKeyDictionary()
  227.         
  228.         self._savepoint_index += 1
  229.         self._savepoint2index[savepoint] = self._savepoint_index
  230.         return savepoint
  231.  
  232.     
  233.     def _remove_and_invalidate_after(self, savepoint):
  234.         savepoint2index = self._savepoint2index
  235.         index = savepoint2index[savepoint]
  236.         for savepoint, i in savepoint2index.items():
  237.             if i > index:
  238.                 savepoint.transaction = None
  239.                 del savepoint2index[savepoint]
  240.                 continue
  241.         
  242.  
  243.     
  244.     def _invalidate_all_savepoints(self):
  245.         for savepoint in self._savepoint2index.keys():
  246.             savepoint.transaction = None
  247.         
  248.         self._savepoint2index.clear()
  249.  
  250.     
  251.     def register(self, obj):
  252.         manager = getattr(obj, '_p_jar', obj)
  253.         if manager is None:
  254.             raise ValueError('Register with no manager')
  255.         
  256.         adapter = self._adapters.get(manager)
  257.         if adapter is None:
  258.             adapter = MultiObjectResourceAdapter(manager)
  259.             adapter.objects.append(obj)
  260.             self._adapters[manager] = adapter
  261.             self.join(adapter)
  262.         elif not id(obj) not in map(id, adapter.objects):
  263.             raise AssertionError
  264.         adapter.objects.append(obj)
  265.  
  266.     
  267.     def commit(self, subtransaction = _marker, deprecation_wng = True):
  268.         if subtransaction is _marker:
  269.             subtransaction = 0
  270.         elif deprecation_wng:
  271.             deprecated37 = deprecated37
  272.             import ZODB.utils
  273.             deprecated37('subtransactions are deprecated; instead of transaction.commit(1), use transaction.savepoint(optimistic=True) in contexts where a subtransaction abort will never occur, or sp=transaction.savepoint() if later rollback is possible and then sp.rollback() instead of transaction.abort(1)')
  274.         
  275.         if self._savepoint2index:
  276.             self._invalidate_all_savepoints()
  277.         
  278.         if subtransaction:
  279.             self._subtransaction_savepoint = self.savepoint(optimistic = True)
  280.             return None
  281.         
  282.         if self.status is Status.COMMITFAILED:
  283.             self._prior_operation_failed()
  284.         
  285.         self._callBeforeCommitHooks()
  286.         self._synchronizers.map((lambda s: s.beforeCompletion(self)))
  287.         self.status = Status.COMMITTING
  288.         
  289.         try:
  290.             self._commitResources()
  291.         except:
  292.             self._saveCommitishError()
  293.  
  294.         self.status = Status.COMMITTED
  295.         if self._manager:
  296.             self._manager.free(self)
  297.         
  298.         self._synchronizers.map((lambda s: s.afterCompletion(self)))
  299.         self.log.debug('commit')
  300.  
  301.     
  302.     def _saveCommitishError(self):
  303.         self.status = Status.COMMITFAILED
  304.         ft = self._failure_traceback = StringIO()
  305.         (t, v, tb) = sys.exc_info()
  306.         traceback.print_stack(sys._getframe(1), None, ft)
  307.         traceback.print_tb(tb, None, ft)
  308.         ft.writelines(traceback.format_exception_only(t, v))
  309.         raise t, v, tb
  310.  
  311.     
  312.     def getBeforeCommitHooks(self):
  313.         return iter(self._before_commit)
  314.  
  315.     
  316.     def addBeforeCommitHook(self, hook, args = (), kws = None):
  317.         if kws is None:
  318.             kws = { }
  319.         
  320.         self._before_commit.append((hook, tuple(args), kws))
  321.  
  322.     
  323.     def beforeCommitHook(self, hook, *args, **kws):
  324.         deprecated38 = deprecated38
  325.         import ZODB.utils
  326.         deprecated38('Use addBeforeCommitHook instead of beforeCommitHook.')
  327.         self.addBeforeCommitHook(hook, args, kws)
  328.  
  329.     
  330.     def _callBeforeCommitHooks(self):
  331.         for hook, args, kws in self._before_commit:
  332.             hook(*args, **kws)
  333.         
  334.         self._before_commit = []
  335.  
  336.     
  337.     def _commitResources(self):
  338.         L = list(self._resources)
  339.         L.sort(rm_cmp)
  340.         
  341.         try:
  342.             for rm in L:
  343.                 rm.tpc_begin(self)
  344.             
  345.             for rm in L:
  346.                 rm.commit(self)
  347.                 self.log.debug('commit %r' % rm)
  348.             
  349.             for rm in L:
  350.                 rm.tpc_vote(self)
  351.                 self._voted[id(rm)] = True
  352.             
  353.             
  354.             try:
  355.                 for rm in L:
  356.                     rm.tpc_finish(self)
  357.             except:
  358.                 self.log.critical('A storage error occurred during the second phase of the two-phase commit.  Resources may be in an inconsistent state.')
  359.                 raise 
  360.  
  361.         except:
  362.             (t, v, tb) = sys.exc_info()
  363.             
  364.             try:
  365.                 self._cleanup(L)
  366.             finally:
  367.                 self._synchronizers.map((lambda s: s.afterCompletion(self)))
  368.  
  369.             raise t, v, tb
  370.  
  371.  
  372.     
  373.     def _cleanup(self, L):
  374.         for rm in L:
  375.             if id(rm) not in self._voted:
  376.                 
  377.                 try:
  378.                     rm.abort(self)
  379.                 except Exception:
  380.                     self.log.error('Error in abort() on manager %s', rm, exc_info = sys.exc_info())
  381.                 except:
  382.                     None<EXCEPTION MATCH>Exception
  383.                 
  384.  
  385.             None<EXCEPTION MATCH>Exception
  386.         
  387.         for rm in L:
  388.             
  389.             try:
  390.                 rm.tpc_abort(self)
  391.             continue
  392.             except Exception:
  393.                 self.log.error('Error in tpc_abort() on manager %s', rm, exc_info = sys.exc_info())
  394.                 continue
  395.             
  396.  
  397.         
  398.  
  399.     
  400.     def abort(self, subtransaction = _marker, deprecation_wng = True):
  401.         if subtransaction is _marker:
  402.             subtransaction = 0
  403.         elif deprecation_wng:
  404.             deprecated37 = deprecated37
  405.             import ZODB.utils
  406.             deprecated37('subtransactions are deprecated; use sp.rollback() instead of transaction.abort(1), where `sp` is the corresponding savepoint captured earlier')
  407.         
  408.         if subtransaction:
  409.             if not self._subtransaction_savepoint:
  410.                 raise interfaces.InvalidSavepointRollbackError
  411.             
  412.             if self._subtransaction_savepoint.valid:
  413.                 self._subtransaction_savepoint.rollback()
  414.                 self._subtransaction_savepoint.transaction = None
  415.                 if not not (self._subtransaction_savepoint.valid):
  416.                     raise AssertionError
  417.             
  418.             return None
  419.         
  420.         if self._savepoint2index:
  421.             self._invalidate_all_savepoints()
  422.         
  423.         self._synchronizers.map((lambda s: s.beforeCompletion(self)))
  424.         tb = None
  425.         for rm in self._resources:
  426.             
  427.             try:
  428.                 rm.abort(self)
  429.             continue
  430.             if tb is None:
  431.                 (t, v, tb) = sys.exc_info()
  432.             
  433.  
  434.             self.log.error('Failed to abort resource manager: %s', rm, exc_info = sys.exc_info())
  435.         
  436.         if self._manager:
  437.             self._manager.free(self)
  438.         
  439.         self._synchronizers.map((lambda s: s.afterCompletion(self)))
  440.         self.log.debug('abort')
  441.         if tb is not None:
  442.             raise t, v, tb
  443.         
  444.  
  445.     
  446.     def note(self, text):
  447.         text = text.strip()
  448.         if self.description:
  449.             self.description += '\n\n' + text
  450.         else:
  451.             self.description = text
  452.  
  453.     
  454.     def setUser(self, user_name, path = '/'):
  455.         self.user = '%s %s' % (path, user_name)
  456.  
  457.     
  458.     def setExtendedInfo(self, name, value):
  459.         self._extension[name] = value
  460.  
  461.  
  462.  
  463. class MultiObjectResourceAdapter(object):
  464.     '''Adapt the old-style register() call to the new-style join().
  465.  
  466.     With join(), a resource mananger like a Connection registers with
  467.     the transaction manager.  With register(), an individual object
  468.     is passed to register().
  469.     '''
  470.     
  471.     def __init__(self, jar):
  472.         self.manager = jar
  473.         self.objects = []
  474.         self.ncommitted = 0
  475.  
  476.     
  477.     def __repr__(self):
  478.         return '<%s for %s at %s>' % (self.__class__.__name__, self.manager, id(self))
  479.  
  480.     
  481.     def sortKey(self):
  482.         return self.manager.sortKey()
  483.  
  484.     
  485.     def tpc_begin(self, txn):
  486.         self.manager.tpc_begin(txn)
  487.  
  488.     
  489.     def tpc_finish(self, txn):
  490.         self.manager.tpc_finish(txn)
  491.  
  492.     
  493.     def tpc_abort(self, txn):
  494.         self.manager.tpc_abort(txn)
  495.  
  496.     
  497.     def commit(self, txn):
  498.         for o in self.objects:
  499.             self.manager.commit(o, txn)
  500.             self.ncommitted += 1
  501.         
  502.  
  503.     
  504.     def tpc_vote(self, txn):
  505.         self.manager.tpc_vote(txn)
  506.  
  507.     
  508.     def abort(self, txn):
  509.         tb = None
  510.         for o in self.objects:
  511.             
  512.             try:
  513.                 self.manager.abort(o, txn)
  514.             continue
  515.             if tb is None:
  516.                 (t, v, tb) = sys.exc_info()
  517.             
  518.  
  519.             txn.log.error('Failed to abort object: %s', object_hint(o), exc_info = sys.exc_info())
  520.         
  521.         if tb is not None:
  522.             raise t, v, tb
  523.         
  524.  
  525.  
  526.  
  527. def rm_cmp(rm1, rm2):
  528.     return cmp(rm1.sortKey(), rm2.sortKey())
  529.  
  530.  
  531. def object_hint(o):
  532.     '''Return a string describing the object.
  533.  
  534.     This function does not raise an exception.
  535.     '''
  536.     oid_repr = oid_repr
  537.     import ZODB.utils
  538.     klass = o.__class__.__name__
  539.     oid = getattr(o, '_p_oid', _marker)
  540.     if oid is not _marker:
  541.         oid = oid_repr(oid)
  542.     
  543.     return '%s oid=%s' % (klass, oid)
  544.  
  545.  
  546. class DataManagerAdapter(object):
  547.     '''Adapt zodb 4-style data managers to zodb3 style
  548.  
  549.     Adapt transaction.interfaces.IDataManager to
  550.     ZODB.interfaces.IPureDatamanager
  551.     '''
  552.     
  553.     def __init__(self, datamanager):
  554.         self._datamanager = datamanager
  555.  
  556.     
  557.     def commit(self, transaction):
  558.         pass
  559.  
  560.     
  561.     def abort(self, transaction):
  562.         self._datamanager.abort(transaction)
  563.  
  564.     
  565.     def tpc_begin(self, transaction):
  566.         pass
  567.  
  568.     
  569.     def tpc_abort(self, transaction):
  570.         self._datamanager.abort(transaction)
  571.  
  572.     
  573.     def tpc_finish(self, transaction):
  574.         self._datamanager.commit(transaction)
  575.  
  576.     
  577.     def tpc_vote(self, transaction):
  578.         self._datamanager.prepare(transaction)
  579.  
  580.     
  581.     def sortKey(self):
  582.         return self._datamanager.sortKey()
  583.  
  584.  
  585.  
  586. class Savepoint:
  587.     '''Transaction savepoint.
  588.  
  589.     Transaction savepoints coordinate savepoints for data managers
  590.     participating in a transaction.
  591.     '''
  592.     interface.implements(interfaces.ISavepoint)
  593.     valid = property((lambda self: self.transaction is not None))
  594.     
  595.     def __init__(self, transaction, optimistic, *resources):
  596.         self.transaction = transaction
  597.         self._savepoints = savepoints = []
  598.         for datamanager in resources:
  599.             
  600.             try:
  601.                 savepoint = datamanager.savepoint
  602.             except AttributeError:
  603.                 if not optimistic:
  604.                     raise TypeError('Savepoints unsupported', datamanager)
  605.                 
  606.                 savepoint = NoRollbackSavepoint(datamanager)
  607.  
  608.             savepoint = savepoint()
  609.             savepoints.append(savepoint)
  610.         
  611.  
  612.     
  613.     def rollback(self):
  614.         transaction = self.transaction
  615.         if transaction is None:
  616.             raise interfaces.InvalidSavepointRollbackError
  617.         
  618.         transaction._remove_and_invalidate_after(self)
  619.         
  620.         try:
  621.             for savepoint in self._savepoints:
  622.                 savepoint.rollback()
  623.         except:
  624.             transaction._saveCommitishError()
  625.  
  626.  
  627.  
  628.  
  629. class AbortSavepoint:
  630.     
  631.     def __init__(self, datamanager, transaction):
  632.         self.datamanager = datamanager
  633.         self.transaction = transaction
  634.  
  635.     
  636.     def rollback(self):
  637.         self.datamanager.abort(self.transaction)
  638.  
  639.  
  640.  
  641. class NoRollbackSavepoint:
  642.     
  643.     def __init__(self, datamanager):
  644.         self.datamanager = datamanager
  645.  
  646.     
  647.     def rollback(self):
  648.         raise TypeError('Savepoints unsupported', self.datamanager)
  649.  
  650.  
  651.